Reactã®forwardRefããã¹ã¿ãŒããŸãããããªãã¡ã¬ã³ã¹è»¢éãçè§£ããåã³ã³ããŒãã³ãã®DOMããŒãã«ã¢ã¯ã»ã¹ããåå©çšå¯èœãªã³ã³ããŒãã³ããäœæããã³ãŒãã®ä¿å®æ§ãé«ããŸãã
React forwardRef: ãªãã¡ã¬ã³ã¹è»¢éã®å æ¬çãªã¬ã€ã
Reactã§ã¯ãåã³ã³ããŒãã³ãã®DOMããŒãã«çŽæ¥ã¢ã¯ã»ã¹ããããšã¯é£ããå ŽåããããŸããããã§forwardRefãç»å Žããåã³ã³ããŒãã³ãã«refã転éããã¡ã«ããºã ãæäŸããŸãããã®èšäºã§ã¯ãforwardRefãæ·±ãæãäžãããã®ç®çãäœ¿ãæ¹ãå©ç¹ã説æããReactãããžã§ã¯ãã§å¹æçã«æŽ»çšããããã®ç¥èã身ã«ä»ããŸãã
forwardRefãšã¯ïŒ
forwardRefã¯ãReact APIã§ããã芪ã³ã³ããŒãã³ããåã³ã³ããŒãã³ãå
ã®DOMããŒããžã®refãåãåãããšãã§ããŸããforwardRefããªãå Žåãrefã¯éåžžãäœæãããã³ã³ããŒãã³ãã«éå®ãããŸãããã®å¶éã«ãããåã³ã³ããŒãã³ãã®åºç€ãšãªãDOMã芪ããçŽæ¥æäœããããšãå°é£ã«ãªãå¯èœæ§ããããŸãã
äŸãããªããã«ã¹ã¿ã å
¥åã³ã³ããŒãã³ãããããã³ã³ããŒãã³ãã®ããŠã³ãæã«èªåçã«å
¥åãã£ãŒã«ãã«ãã©ãŒã«ã¹ããããšããŸããforwardRefããªãå Žåã芪ã³ã³ããŒãã³ãã¯å
¥åã®DOMããŒãã«çŽæ¥ã¢ã¯ã»ã¹ããæ¹æ³ããããŸãããforwardRefã䜿çšãããšã芪ã¯å
¥åãã£ãŒã«ããžã®åç
§ãä¿æãããã®focus()ã¡ãœãããåŒã³åºãããšãã§ããŸãã
forwardRefã䜿çšããçç±
forwardRefãéåžžã«åœ¹ç«ã€äžè¬çãªã·ããªãªãããã€ã玹ä»ããŸãã
- åDOMããŒããžã®ã¢ã¯ã»ã¹ïŒãããäž»ãªäœ¿çšäŸã§ãã芪ã³ã³ããŒãã³ãã¯ãåã³ã³ããŒãã³ãå ã®DOMããŒããçŽæ¥æäœãŸãã¯å¯Ÿè©±ã§ããŸãã
- åå©çšå¯èœãªã³ã³ããŒãã³ãã®äœæïŒrefã転éããããšã§ãã¢ããªã±ãŒã·ã§ã³ã®ããŸããŸãªéšåã«ç°¡åã«çµ±åã§ãããããæè»ã§åå©çšå¯èœãªã³ã³ããŒãã³ããäœæã§ããŸãã
- ãµãŒãããŒãã£ã©ã€ãã©ãªãšã®çµ±åïŒäžéšã®ãµãŒãããŒãã£ã©ã€ãã©ãªã§ã¯ãDOMããŒããžã®çŽæ¥ã¢ã¯ã»ã¹ãå¿
èŠã§ãã
forwardRefã䜿çšãããšããããã®ã©ã€ãã©ãªãReactã³ã³ããŒãã³ãã«ã·ãŒã ã¬ã¹ã«çµ±åã§ããŸãã - ãã©ãŒã«ã¹ãšéžæã®ç®¡çïŒåè¿°ã®ããã«ã
forwardRefã䜿çšãããšãè€éãªã³ã³ããŒãã³ãéå±€å ã§ã®ãã©ãŒã«ã¹ãšéžæã®ç®¡çãã¯ããã«ç°¡åã«ãªããŸãã
forwardRefã®ä»çµã¿
forwardRefã¯ãé«éã³ã³ããŒãã³ãïŒHOCïŒã§ããåŒæ°ãšããŠã¬ã³ããªã³ã°é¢æ°ãåããReactã³ã³ããŒãã³ããè¿ããŸããã¬ã³ããªã³ã°é¢æ°ã¯ãåŒæ°ãšããŠpropsãšrefãåãåããŸããrefåŒæ°ã¯ã芪ã³ã³ããŒãã³ããæž¡ãrefã§ããã¬ã³ããªã³ã°é¢æ°å
ã§ã¯ããã®refãåã³ã³ããŒãã³ãå
ã®DOMããŒãã«ã¢ã¿ããã§ããŸãã
åºæ¬çãªæ§æ
forwardRefã®åºæ¬çãªæ§æã¯æ¬¡ã®ãšããã§ãã
const MyComponent = React.forwardRef((props, ref) => {
// ã³ã³ããŒãã³ãããžãã¯ã¯ããã«
return <div ref={ref}>...</div>;
});
ãã®æ§æãåè§£ããŠã¿ãŸãããã
React.forwardRef()ïŒããã¯ã³ã³ããŒãã³ããã©ãããã颿°ã§ãã(props, ref) => { ... }ïŒããã¯ã¬ã³ããªã³ã°é¢æ°ã§ããã³ã³ããŒãã³ãã®propsãšã芪ããæž¡ãããrefãåãåããŸãã<div ref={ref}>...</div>ïŒããã¯éæ³ãèµ·ããå Žæã§ããåä¿¡ããrefãã³ã³ããŒãã³ãå ã®DOMããŒãã«ã¢ã¿ããããŸããããã«ããããã®DOMããŒãã¯èŠªã³ã³ããŒãã³ãããã¢ã¯ã»ã¹ã§ããããã«ãªããŸãã
å®è·µçãªäŸ
forwardRefãå®éã®ã·ããªãªã§ã©ã®ããã«äœ¿çšã§ãããã説æããããã«ãããã€ãã®å®è·µçãªäŸãèŠãŠã¿ãŸãããã
äŸ1ïŒå ¥åãã£ãŒã«ããžã®ãã©ãŒã«ã¹
ãã®äŸã§ã¯ãããŠã³ãæã«èªåçã«å ¥åãã£ãŒã«ãã«ãã©ãŒã«ã¹ããã«ã¹ã¿ã å ¥åã³ã³ããŒãã³ããäœæããŸãã
import React, { useRef, useEffect } from 'react';
const FancyInput = React.forwardRef((props, ref) => {
return (
<input ref={ref} type="text" className="fancy-input" {...props} />
);
});
function ParentComponent() {
const inputRef = useRef(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
return (
<FancyInput ref={inputRef} placeholder="ãã©ãŒã«ã¹ããŠãã ããïŒ" />
);
}
export default ParentComponent;
説æïŒ
FancyInputã¯ãReact.forwardRefã䜿çšããŠäœæãããŸããpropsãšrefãåãåããŸããrefã¯<input>èŠçŽ ã«ã¢ã¿ãããããŸããParentComponentã¯ãuseRefã䜿çšããŠrefãäœæããŸããrefã¯FancyInputã«æž¡ãããŸããuseEffectããã¯ã§ã¯ãã³ã³ããŒãã³ãã®ããŠã³ãæã«å ¥åãã£ãŒã«ãã«ãã©ãŒã«ã¹ãèšå®ãããŸãã
äŸ2ïŒãã©ãŒã«ã¹ç®¡çãåããã«ã¹ã¿ã ãã¿ã³
芪ããã©ãŒã«ã¹ãå¶åŸ¡ã§ããã«ã¹ã¿ã ãã¿ã³ã³ã³ããŒãã³ããäœæããŸãããã
import React, { forwardRef } from 'react';
const MyButton = forwardRef((props, ref) => {
return (
<button ref={ref} className="my-button" {...props}>
{props.children}
</button>
);
});
function App() {
const buttonRef = React.useRef(null);
const focusButton = () => {
if (buttonRef.current) {
buttonRef.current.focus();
}
};
return (
<div>
<MyButton ref={buttonRef} onClick={() => alert('ãã¿ã³ãã¯ãªãã¯ãããŸããïŒ')}>
ã¯ãªãã¯ããŠãã ãã
</MyButton>
<button onClick={focusButton}>ãã©ãŒã«ã¹ãã¿ã³</button>
</div>
);
}
export default App;
説æïŒ
MyButtonã¯ãforwardRefã䜿çšããŠãrefããã¿ã³èŠçŽ ã«è»¢éããŸãã- 芪ã³ã³ããŒãã³ãïŒ
AppïŒã¯ãuseRefã䜿çšããŠrefãäœæãããããMyButtonã«æž¡ããŸãã focusButton颿°ã䜿çšãããšã芪ã¯ããã°ã©ã ã§ãã¿ã³ã«ãã©ãŒã«ã¹ã§ããŸãã
äŸ3ïŒãµãŒãããŒãã£ã©ã€ãã©ãªãšã®çµ±åïŒäŸïŒreact-selectïŒ
å€ãã®ãµãŒãããŒãã£ã©ã€ãã©ãªã§ã¯ãåºç€ãšãªãDOMããŒããžã®ã¢ã¯ã»ã¹ãå¿
èŠã§ããreact-selectã䜿çšããä»®æ³ã·ããªãªã§forwardRefãçµ±åããæ¹æ³ã瀺ããŸããããããã§ã¯ãselectã®å
¥åèŠçŽ ã«ã¢ã¯ã»ã¹ããå¿
èŠããããããããŸããã
泚ïŒããã¯ç°¡ç¥åãããä»®æ³çãªèª¬æã§ããã³ã³ããŒãã³ãã«ã¢ã¯ã»ã¹ããŠã«ã¹ã¿ãã€ãºããããã®æ£åŒã«ãµããŒããããŠããæ¹æ³ã«ã€ããŠã¯ãå®éã®react-selectã®ããã¥ã¡ã³ããåç
§ããŠãã ããã
import React, { useRef, useEffect } from 'react';
// ãã¢ã³ã¹ãã¬ãŒã·ã§ã³çšã«ç°¡ç¥åãããreact-selectã€ã³ã¿ãŒãã§ã€ã¹ãæ³å®
import Select from 'react-select'; // å®éã®ã€ã³ããŒãã«çœ®ãæããŸã
const CustomSelect = React.forwardRef((props, ref) => {
return (
<Select ref={ref} {...props} />
);
});
function MyComponent() {
const selectRef = useRef(null);
useEffect(() => {
// ä»®æ³ïŒreact-selectå
ã®å
¥åèŠçŽ ãžã®ã¢ã¯ã»ã¹
if (selectRef.current && selectRef.current.inputRef) { // inputRefã¯ä»®æ³ããããã£ã§ã
console.log('å
¥åèŠçŽ ïŒ', selectRef.current.inputRef.current);
}
}, []);
return (
<CustomSelect
ref={selectRef}
options={[
{ value: 'chocolate', label: 'ãã§ã³ã¬ãŒã' },
{ value: 'strawberry', label: 'ã¹ããããªãŒ' },
{ value: 'vanilla', label: 'ããã©' },
]}
/>
);
}
export default MyComponent;
ãµãŒãããŒãã£ã©ã€ãã©ãªã«é¢ããéèŠãªèæ ®äºé ïŒ
- ã©ã€ãã©ãªã®ããã¥ã¡ã³ããåç §ããïŒå éšã³ã³ããŒãã³ãã«ã¢ã¯ã»ã¹ããŠæäœããããã®æšå¥šæ¹æ³ãçè§£ããããã«ãåžžã«ãµãŒãããŒãã£ã©ã€ãã©ãªã®ããã¥ã¡ã³ãã確èªããŠãã ãããææžåãããŠããªããŸãã¯ãµããŒããããŠããªãæ¹æ³ã䜿çšãããšãäºæããªãåäœãçºçããããå°æ¥ã®ããŒãžã§ã³ã§ç Žæãããããå¯èœæ§ããããŸãã
- ã¢ã¯ã»ã·ããªãã£ïŒDOMããŒãã«çŽæ¥ã¢ã¯ã»ã¹ããå Žåã¯ãã¢ã¯ã»ã·ããªãã£æšæºãç¶æããŠãã ãããæ¯æŽæè¡ã䜿çšãããŠãŒã¶ãŒãã³ã³ããŒãã³ããæäœã§ããä»£æ¿ææ®µãæäŸããŸãã
ãã¹ããã©ã¯ãã£ã¹ãšèæ ®äºé
forwardRefã¯åŒ·åãªããŒã«ã§ãããæ
éã«äœ¿çšããããšãäžå¯æ¬ ã§ããçæãã¹ããã¹ããã©ã¯ãã£ã¹ãšèæ
®äºé
ã以äžã«ç€ºããŸãã
- é床ã®äœ¿çšãé¿ããïŒããç°¡åãªä»£æ¿ææ®µãããå Žåã¯ã
forwardRefã䜿çšããªãã§ãã ãããã³ã³ããŒãã³ãéã®éä¿¡ã«ã¯ãpropsãŸãã¯ã³ãŒã«ããã¯é¢æ°ã䜿çšããããšãæ€èšããŠãã ãããforwardRefãé床ã«äœ¿çšãããšãã³ãŒãã®çè§£ãšä¿å®ãé£ãããªãå¯èœæ§ããããŸãã - ã«ãã»ã«åã®ç¶æïŒã«ãã»ã«åãç Žãããšã«æ³šæããŠãã ãããåã³ã³ããŒãã³ãã®DOMããŒããçŽæ¥æäœãããšãã³ãŒããããè匱ã«ãªãããªãã¡ã¯ã¿ãªã³ã°ãé£ãããªãå¯èœæ§ããããŸããå¯èœãªéãDOMã®çŽæ¥æäœãæå°éã«æããã³ã³ããŒãã³ãã®å éšAPIã«äŸåããããã«åªããŠãã ããã
- ã¢ã¯ã»ã·ããªãã£ïŒrefsããã³DOMããŒããæäœããå Žåã¯ãåžžã«ã¢ã¯ã»ã·ããªãã£ãåªå ããŠãã ãããã³ã³ããŒãã³ããé害ã®ãã人ã«äœ¿çšã§ããããã«ããŸããã»ãã³ãã£ãã¯HTMLã䜿çšããé©åãªARIA屿§ãæäŸããæ¯æŽæè¡ã䜿çšããŠã³ã³ããŒãã³ãããã¹ãããŸãã
- ã³ã³ããŒãã³ãã®ã©ã€ããµã€ã¯ã«ãçè§£ããïŒrefããã€å©çšå¯èœã«ãªãããèªèããŸããrefã¯éåžžãã³ã³ããŒãã³ããããŠã³ããããåŸã«å©çšå¯èœã«ãªããŸããã³ã³ããŒãã³ããã¬ã³ããªã³ã°ãããåŸã«refã«ã¢ã¯ã»ã¹ããã«ã¯ã
useEffectã䜿çšããŸãã - TypeScriptã§äœ¿çšããïŒTypeScriptã䜿çšããŠããå Žåã¯ã
forwardRefã䜿çšããrefsããã³ã³ã³ããŒãã³ããé©åã«åä»ãããŠãã ãããããã«ãããæ©æã«ãšã©ãŒããã£ããããã³ãŒãå šäœã®åå®å šæ§ãåäžãããããšãã§ããŸãã
forwardRefã®ä»£æ¿æ¡
å Žåã«ãã£ãŠã¯ãforwardRefã䜿çšãã代ããã«ãããé©åãªä»£æ¿ææ®µããããŸãã
- Propsãšã³ãŒã«ããã¯ïŒpropsãä»ããŠããŒã¿ãšåäœãæž¡ãããšã¯ãã³ã³ããŒãã³ãéã§éä¿¡ããæãã·ã³ãã«ã§æãæšå¥šãããæ¹æ³ã§ããããšããããããŸããåã§ããŒã¿ãæž¡ããã颿°ãããªã¬ãŒããã ãã§ããå Žåã¯ãéåžžãpropsãšã³ãŒã«ããã¯ãæé©ã§ãã
- ã³ã³ããã¹ãïŒæ·±ããã¹ããããã³ã³ããŒãã³ãéã§ããŒã¿ãå ±æããã«ã¯ãReactã®Context APIãé©åãªä»£æ¿ææ®µã«ãªãå¯èœæ§ããããŸããã³ã³ããã¹ãã䜿çšãããšããã¹ãŠã®ã¬ãã«ã§propsãæåã§æž¡ãããšãªããã³ã³ããŒãã³ãã®ãµãããªãŒå šäœã«ããŒã¿ãæäŸã§ããŸãã
- åœä»€åãã³ãã«ïŒuseImperativeHandleããã¯ã¯ãforwardRefãšçµã¿åãããŠäœ¿çšããŠãDOMããŒãå šäœãå ¬éããã®ã§ã¯ãªããå¶éãããå¶åŸ¡ãããAPIã芪ã³ã³ããŒãã³ãã«å ¬éã§ããŸããããã«ãããã«ãã»ã«åãåäžããŸãã
é«åºŠãªäœ¿çšæ³ïŒuseImperativeHandle
useImperativeHandleããã¯ã䜿çšãããšãforwardRefã䜿çšãããšãã«èŠªã³ã³ããŒãã³ãã«å
¬éãããã€ã³ã¹ã¿ã³ã¹å€ãã«ã¹ã¿ãã€ãºã§ããŸããããã«ããã芪ã³ã³ããŒãã³ããã¢ã¯ã»ã¹ã§ãããã®ããã詳现ã«å¶åŸ¡ã§ããã«ãã»ã«åãåäžããŸãã
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
getValue: () => {
return inputRef.current.value;
},
}));
return <input ref={inputRef} type="text" {...props} />;
});
function ParentComponent() {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.focus();
};
const handleGetValue = () => {
alert(inputRef.current.getValue());
};
return (
<div>
<FancyInput ref={inputRef} placeholder="ããã¹ããå
¥å" />
<button onClick={handleFocus}>ãã©ãŒã«ã¹å
¥å</button>
<button onClick={handleGetValue}>å€ãååŸ</button>
</div>
);
}
export default ParentComponent;
説æïŒ
FancyInputã³ã³ããŒãã³ãã¯ãuseRefã䜿çšããŠãå ¥åèŠçŽ ã®å éšrefïŒinputRefïŒãäœæããŸããuseImperativeHandleã¯ã転éãããrefãä»ããŠèŠªã³ã³ããŒãã³ãã«å ¬éãããã«ã¹ã¿ã ãªããžã§ã¯ããå®çŸ©ããããã«äœ¿çšãããŸãããã®å Žåãfocus颿°ãšgetValue颿°ãå ¬éããŠããŸãã- 芪ã³ã³ããŒãã³ãã¯ãå ¥åèŠçŽ ã®DOMããŒãã«çŽæ¥ã¢ã¯ã»ã¹ããã«ãrefãä»ããŠãããã®é¢æ°ãåŒã³åºãããšãã§ããŸãã
äžè¬çãªåé¡ã®ãã©ãã«ã·ã¥ãŒãã£ã³ã°
forwardRefã®äœ¿çšæã«çºçããå¯èœæ§ã®ããäžè¬çãªåé¡ãšããã®ãã©ãã«ã·ã¥ãŒãã£ã³ã°æ¹æ³ã以äžã«ç€ºããŸãã
- Refãnullã§ãïŒrefã芪ã³ã³ããŒãã³ãããæ£ããæž¡ãããåã³ã³ããŒãã³ããDOMããŒãã«refãæ£ããã¢ã¿ããããŠããããšã確èªããŸãããŸããã³ã³ããŒãã³ããããŠã³ããããåŸïŒããšãã°ã
useEffectããã¯å ïŒã«refã«ã¢ã¯ã»ã¹ããŠããããšã確èªããŠãã ããã - nullã®ããããã£'focus'ãèªã¿åããŸããïŒããã¯éåžžãrefãDOMããŒãã«æ£ããã¢ã¿ãããããŠããªãããDOMããŒãããŸã ã¬ã³ããªã³ã°ãããŠããªãããšã瀺ããŸããã³ã³ããŒãã³ãæ§é ãå確èªããrefãæ£ããèŠçŽ ã«ã¢ã¿ãããããŠããããšã確èªããŠãã ããã
- TypeScriptã®åãšã©ãŒïŒrefsãæ£ããåä»ããããŠããããšã確èªããŠãã ããã
React.RefObject<HTMLInputElement>ïŒãŸãã¯é©åãªHTMLèŠçŽ ã®åïŒã䜿çšããŠãrefã®åãå®çŸ©ããŸãããŸããforwardRefã䜿çšããã³ã³ããŒãã³ããReact.forwardRef<HTMLInputElement, Props>ã§æ£ããåä»ããããŠããããšã確èªããŠãã ããã - äºæããªãåäœïŒäºæããªãåäœãçºçããŠããå Žåã¯ãã³ãŒããæ³šææ·±ã確èªããReactã®ã¬ã³ããªã³ã°ããã»ã¹ã劚ããå¯èœæ§ã®ããæ¹æ³ã§DOMã誀ã£ãŠæäœããŠããªãããšã確èªããŠãã ãããReact DevToolsã䜿çšããŠã³ã³ããŒãã³ãããªãŒã調ã¹ãŠãæœåšçãªåé¡ãç¹å®ããŸãã
çµè«
forwardRefã¯ãReactéçºè
ã®æŠåšåº«ã«ãã貎éãªããŒã«ã§ãã芪ã³ã³ããŒãã³ããšåã³ã³ããŒãã³ãã®éã®ã®ã£ãããåããçŽæ¥DOMæäœãå¯èœã«ããã³ã³ããŒãã³ãã®åå©çšæ§ãé«ããŸãããã®ç®çãäœ¿ãæ¹ããã¹ããã©ã¯ãã£ã¹ãçè§£ããããšã§ãforwardRefãæŽ»çšããŠããã匷åã§æè»æ§ããããä¿å®å¯èœãªReactã¢ããªã±ãŒã·ã§ã³ãäœæã§ããŸããæ
éã«äœ¿çšããã¢ã¯ã»ã·ããªãã£ãåªå
ããå¯èœãªéãåžžã«ã«ãã»ã«åãç¶æããããã«åªããŠãã ããã
ãã®å
æ¬çãªã¬ã€ãã§ã¯ãReactãããžã§ã¯ãã§forwardRefãèªä¿¡ãæã£ãŠå®è£
ããããã®ç¥èãšäŸãæäŸããŸãããããããŒã³ãŒãã£ã³ã°ïŒ